/* 
 * Copyright (c) 2008 Intel Corporation
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * -- Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * -- Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * -- Neither the name of the Intel Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE INTEL OR ITS
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Net;
using System.IO;
using System.Text;
using System.Xml;
using OpenMetaverse;
using OpenMetaverse.StructuredData;

namespace AssetClient
{
    public static class AssetClient
    {
        public static bool TryGetAuthToken(Uri claimedIdentifier, string firstName, string lastName, string password,
            Uri serverUrl, out UUID authToken)
        {
            authToken = UUID.Zero;
            serverUrl = new Uri(serverUrl, "authenticate");

            OSDMap osd = new OSDMap(1);
            osd.Add("identifier", OSD.FromUri(claimedIdentifier));
            byte[] postData = Encoding.UTF8.GetBytes(OSDParser.SerializeJson(osd).ToJson());

            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serverUrl);
                request.Method = "POST";
                request.ContentLength = postData.Length;
                request.ContentType = "application/json";

                using (Stream stream = request.GetRequestStream())
                    stream.Write(postData, 0, postData.Length);

                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                if (response.ResponseUri.Authority == claimedIdentifier.Authority)
                {
                    // This response came from the OpenID provider (user server).
                    // Send back a form with our filled in data
                    postData = Encoding.UTF8.GetBytes(
                        String.Format("first={0}&last={1}&pass={2}", firstName, lastName, Utils.MD5(password)));

                    request = (HttpWebRequest)WebRequest.Create(response.ResponseUri);
                    request.Method = "POST";
                    request.ContentLength = postData.Length;
                    request.ContentType = "application/x-www-form-urlencoded";

                    using (Stream stream = request.GetRequestStream())
                        stream.Write(postData, 0, postData.Length);

                    response = (HttpWebResponse)request.GetResponse();

                    if (response.ResponseUri.Authority == serverUrl.Authority)
                    {
                        // This response came from the OpenID consumer (asset server)
                        if (response.StatusCode == HttpStatusCode.OK)
                        {
                            using (Stream stream = response.GetResponseStream())
                            {
                                OSDMap map = OSDParser.DeserializeJson(stream) as OSDMap;

                                if (map != null && map.ContainsKey("auth_token"))
                                {
                                    authToken = map["auth_token"].AsUUID();
                                    return true;
                                }
                            }
                        }
                    }
                    else
                    {
                    }
                }
            }
            catch (WebException ex)
            {
                PrintWebException(ex);
            }

            return false;
        }

        public static bool TryGetAsset(UUID assetID, UUID authToken, Uri assetLocation, out byte[] assetData)
        {
            assetData = null;

            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(assetLocation);
                AddAuthorizeHeader(request, authToken);
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    int contentLength = (int)response.ContentLength;
                    assetData = new byte[contentLength];
                    
                    using (Stream stream = response.GetResponseStream())
                    {
                        int pos = 0;
                        while (pos < contentLength)
                            pos += stream.Read(assetData, pos, contentLength - pos);
                    }

                    return true;
                }
            }
            catch (WebException ex)
            {
                if (ex.Response == null || ((HttpWebResponse)ex.Response).StatusCode != HttpStatusCode.NotFound)
                    PrintWebException(ex);
            }

            return false;
        }

        public static bool TryGetMetadata(UUID assetID, UUID authToken, Uri serverUrl, out byte[] metadata)
        {
            metadata = null;
            serverUrl = new Uri(serverUrl, String.Format("{0}/metadata", assetID));

            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serverUrl);
                AddAuthorizeHeader(request, authToken);
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    int contentLength = (int)response.ContentLength;
                    metadata = new byte[contentLength];

                    using (Stream stream = response.GetResponseStream())
                    {
                        int pos = 0;
                        while (pos < contentLength)
                            pos += stream.Read(metadata, pos, contentLength - pos);
                    }

                    return true;
                }
            }
            catch (WebException ex)
            {
                if (ex.Response == null || ((HttpWebResponse)ex.Response).StatusCode != HttpStatusCode.NotFound)
                    PrintWebException(ex);
            }

            return false;
        }

        public static UUID CreateAsset(string name, string description, string contentType, bool temporary,
            UUID authToken, Uri serverUrl, byte[] assetData, UUID assetID)
        {
            string requestURL = String.Format("{0}{1}", serverUrl, "createasset");

            OSDMap osd = new OSDMap();

            if (assetID != UUID.Zero) osd["id"] = OSD.FromUUID(assetID);
            osd["name"] = OSD.FromString(name);
            osd["description"] = OSD.FromString(description);
            osd["type"] = OSD.FromString(contentType);
            osd["temporary"] = OSD.FromBoolean(temporary);
            osd["data"] = OSD.FromBinary(assetData);

            byte[] postData = Encoding.UTF8.GetBytes(OSDParser.SerializeJson(osd).ToJson());

            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestURL);
                AddAuthorizeHeader(request, authToken);
                request.Method = "POST";
                request.ContentLength = postData.Length;
                request.ContentType = "application/json";

                using (Stream stream = request.GetRequestStream())
                {
                    stream.Write(postData, 0, postData.Length);
                    stream.Flush();
                }

                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                if (response.StatusCode == HttpStatusCode.Created)
                {
                    assetID = UUID.Zero;

                    using (Stream stream = response.GetResponseStream())
                    {
                        OSD osdata = OSDParser.DeserializeJson(stream);
                        if (osdata.Type == OSDType.Map)
                        {
                            OSDMap responseMap = (OSDMap)osdata;
                            if (responseMap.ContainsKey("id"))
                                assetID = responseMap["id"].AsUUID();
                        }
                    }

                    response.Close();
                    return assetID;
                }
                else
                {
                    response.Close();
                    return UUID.Zero;
                }
            }
            catch (WebException ex)
            {
                PrintWebException(ex);
            }

            return UUID.Zero;
        }

        static void AddAuthorizeHeader(HttpWebRequest request, UUID authToken)
        {
            if (authToken != UUID.Zero)
                request.Headers.Add(HttpRequestHeader.Authorization, String.Format("OpenGrid {0}", authToken));
        }

        static void PrintWebException(WebException ex)
        {
            if (ex.Response != null)
            {
                HttpWebResponse response = (HttpWebResponse)ex.Response;

                if (!String.IsNullOrEmpty(response.StatusDescription))
                {
                    Console.WriteLine("[ERROR] The remote server returned an error: " + response.StatusDescription);
                    return;
                }
            }
            
            Console.WriteLine("[ERROR] " + ex.Message);
        }
    }
}
